import json
import logging
from typing import List

from aliyunsdkalidns.request.v20150109 import (
    AddDomainRecordRequest,
    DeleteDomainRecordRequest,
    DescribeDomainRecordsRequest,
    DescribeDomainsRequest,
    SetDomainRecordStatusRequest,
    UpdateDomainRecordRequest,
)
from aliyunsdkcore.client import AcsClient
from aliyunsdkcore.acs_exception.exceptions import ClientException, ServerException

from app.schemas.dns import (
    DNSRecord,
    DNSRecordCreate,
    DNSRecordIdentifier,
    DNSRecordStatus,
    DNSRecordType,
    DNSRecordUpdate,
    DomainSummary,
)
from app.services.cloud_providers.base import BaseProvider

logger = logging.getLogger(__name__)


class AliyunProvider(BaseProvider):
    """阿里云 (Aliyun) DNS 适配器。"""

    def __init__(self, access_key: str, secret_key: str, region: str = "cn-hangzhou") -> None:
        super().__init__(access_key, secret_key, region)
        self.client = AcsClient(access_key, secret_key, region)

    def _do_request(self, request):
        request.set_accept_format("json")
        try:
            response_str = self.client.do_action_with_exception(request)
            return json.loads(response_str)
        except (ClientException, ServerException) as e:
            logger.error(f"Aliyun request failed: {e}")
            raise

    def list_domains(self) -> List[DomainSummary]:
        req = DescribeDomainsRequest.DescribeDomainsRequest()
        # 默认分页 20，这里简化
        req.set_PageSize(100)
        resp = self._do_request(req)
        results = []
        if "Domains" in resp and "Domain" in resp["Domains"]:
            for item in resp["Domains"]["Domain"]:
                results.append(
                    DomainSummary(
                        domain=item["DomainName"],
                        provider="aliyun",
                        record_count=item.get("RecordCount", 0),
                    )
                )
        return results

    def list_records(self, domain: str) -> List[DNSRecord]:
        req = DescribeDomainRecordsRequest.DescribeDomainRecordsRequest()
        req.set_DomainName(domain)
        req.set_PageSize(500)
        resp = self._do_request(req)
        results = []
        if "DomainRecords" in resp and "Record" in resp["DomainRecords"]:
            for item in resp["DomainRecords"]["Record"]:
                results.append(
                    DNSRecord(
                        id=str(item["RecordId"]),
                        provider="aliyun",
                        domain=domain,
                        record_type=DNSRecordType(item["Type"].upper()),
                        name=item["RR"],
                        value=item["Value"],
                        ttl=item["TTL"],
                        status=DNSRecordStatus.active if item["Status"] == "ENABLE" else DNSRecordStatus.paused,
                    )
                )
        return results

    def create_record(self, payload: DNSRecordCreate) -> DNSRecord:
        req = AddDomainRecordRequest.AddDomainRecordRequest()
        req.set_DomainName(payload.domain)
        req.set_RR(payload.name)
        req.set_Type(payload.record_type.value)
        req.set_Value(payload.value)
        req.set_TTL(payload.ttl)
        # Aliyun doesn't support setting status in Create, defaults to Enable.
        
        resp = self._do_request(req)
        record_id = resp["RecordId"]

        if payload.status == DNSRecordStatus.paused:
            self.set_record_status(record_id, DNSRecordStatus.paused)

        return DNSRecord(
            id=str(record_id),
            provider="aliyun",
            domain=payload.domain,
            record_type=payload.record_type,
            name=payload.name,
            value=payload.value,
            ttl=payload.ttl,
            status=payload.status,
        )

    def update_record(self, domain: str, payload: DNSRecordUpdate) -> DNSRecord:
        # Need to fetch current if fields missing
        current_records = self.list_records(domain)
        current = next((r for r in current_records if r.id == payload.id), None)
        if not current:
            raise KeyError("Record not found")

        req = UpdateDomainRecordRequest.UpdateDomainRecordRequest()
        req.set_RecordId(payload.id)
        req.set_RR(payload.name if payload.name else current.name)
        req.set_Type(payload.record_type.value if payload.record_type else current.record_type.value)
        req.set_Value(payload.value if payload.value else current.value)
        req.set_TTL(payload.ttl if payload.ttl else current.ttl)
        
        self._do_request(req)

        if payload.status is not None and payload.status != current.status:
            self.set_record_status(payload.id, payload.status)

        return DNSRecord(
            id=payload.id,
            provider="aliyun",
            domain=domain,
            record_type=DNSRecordType(req.get_Type()),
            name=req.get_RR(),
            value=req.get_Value(),
            ttl=req.get_TTL(),
            status=payload.status if payload.status else current.status,
        )

    def delete_record(self, domain: str, payload: DNSRecordIdentifier) -> None:
        req = DeleteDomainRecordRequest.DeleteDomainRecordRequest()
        req.set_RecordId(payload.id)
        self._do_request(req)

    def batch_delete_records(self, domain: str, record_ids: List[str]) -> int:
        # Aliyun doesn't have batch delete for records easily exposed?
        # Loop delete
        count = 0
        for rid in record_ids:
            try:
                self.delete_record(domain, DNSRecordIdentifier(id=rid))
                count += 1
            except Exception:
                pass
        return count

    def set_record_status(self, record_id: str, status: DNSRecordStatus) -> None:
        req = SetDomainRecordStatusRequest.SetDomainRecordStatusRequest()
        req.set_RecordId(record_id)
        req.set_Status("Enable" if status == DNSRecordStatus.active else "Disable")
        self._do_request(req)
